﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CYTS.Aoyou.Framework.Utility;
using CYTS.Aoyou.Framework.Config;

namespace CYTS.Aoyou.Framework.Database
{
    /// <summary>
    /// 数据库操作适配器工厂
    /// </summary>
    public class DataAdapterFactory
    {
        private static object lockObj = new object();
        private static DataAdapterFactory instance;
        private static IDictionary<string, IDataAdapter> dataAdapterList = new Dictionary<string, IDataAdapter>(64);

        static DataAdapterFactory()
        {
            //ConfigFactory.OnModified += new Config.Config.ModifiedHandler(ConfigFactory_OnModified);
            ConfigFactory.OnModified += new Action(ConfigFactory_OnModified);
        }

        private DataAdapterFactory()
        {

        }

        /// <summary>
        /// 获取对象服务数据访问适配器工具实例
        /// </summary>
        /// <returns></returns>
        public static DataAdapterFactory GetInstance()
        {
            if (instance == null)
            {
                lock (lockObj)
                {
                    if (instance == null)
                    {
                        instance = new DataAdapterFactory();
                    }
                }
            }

            return instance;
        }

        /// <summary>
        /// 获取内存中所有数据库操作适配器
        /// </summary>
        /// <returns></returns>
        public static IList<IDataAdapter> GetAllDataAdapterListInMemory()
        {
            return dataAdapterList.Values.ToList();
        }

        /// <summary>
        /// 获取指定类型的数据访问适配器(默认为SqlServer)
        /// </summary>
        /// <returns></returns>
        public T GetDataAdapter<T>() where T : IDataAdapter
        {
            DatabaseCategory dbCategory = DatabaseCategory.SqlServer;
            DataAccessType daType = DataAccessType.ADO;
            DBCategoryConfig config = ConfigFactory.GetConfig<DBCategoryConfig>();
            Type type = typeof(T);
            string adapterName = type.Name.Substring(1, type.Name.Length - "DataAdapter".Length - 1);
            if (config.DataAdapters == null)
            {
                throw new Exception("配置文件关键元素缺失");
            }
            DataAdapter adapterConfig = config.DataAdapters.FirstOrDefault(p => p.Name == adapterName);
            if (adapterConfig != null)
            {
                dbCategory = adapterConfig.DBCategory;
                daType = adapterConfig.DAType;
            }
            return GetDataAdapter<T>(dbCategory, daType);
        }

        /// <summary>
        /// 获取指定类型的数据访问适配器
        /// </summary>
        /// <param name="dbType">支持的数据库类型</param>
        /// <param name="daType">数据访问类型</param>
        /// <returns></returns>
        public T GetDataAdapter<T>(DatabaseCategory dbType, DataAccessType daType) where T : IDataAdapter
        {
            return (T)LoadDataAdapter(typeof(T), dbType, daType);
        }

        private IDataAdapter LoadDataAdapter(Type adapterInterfaceType, DatabaseCategory dbType, DataAccessType daType)
        {
            IDataAdapter adapter = this.PullAdapterFromCache(adapterInterfaceType, dbType, daType);
            if (adapter == null)
            {
                Type adapterType = GetAdapterType(adapterInterfaceType, dbType, daType);
                if (adapterType == null)
                {
                    throw new Exception("在 \"" + dbType.ToString() + "\"找不到Adapter: \"" + adapterInterfaceType.FullName + "\" !");
                }

                adapter = (IDataAdapter)Activator.CreateInstance(adapterType, GetDBProviderByDataBaseType(dbType));
                this.PushToAdapterCache(adapterInterfaceType, dbType, daType, adapter);
            }

            return (IDataAdapter)adapter;
        }

        private Type GetAdapterType(Type dataAdapterInterfaceType, DatabaseCategory dbType, DataAccessType daType)
        {
            string assemblyName = dataAdapterInterfaceType.Namespace.Substring(0, dataAdapterInterfaceType.Namespace.Length - "DAInterface".Length) + "DataAccess";
            System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyName);
            Type[] types = assembly.GetTypes();

            foreach (Type t in types)
            {
                if (TypeHelper.IsRealizeInterface(t, dataAdapterInterfaceType) && TypeHelper.IsInheritBase(t, typeof(DataAdapterBase)))
                {
                    string match = daType > 0 ? assemblyName + "." + daType.ToString() : assemblyName;
                    if (t.FullName.Contains(match) && t.Name.EndsWith(GetSuffixByDataBaseType(dbType)))
                    {
                        return t;
                    }
                }
            }

            return null;
        }

        private void PushToAdapterCache(Type dataAdapterInterfaceType, DatabaseCategory dbType, DataAccessType daType, IDataAdapter adapter)
        {
            string key = dataAdapterInterfaceType.FullName + "#" + dbType.ToString() + "#" + daType.ToString();
            lock (lockObj)
            {
                if (dataAdapterList.ContainsKey(key) == false)
                {
                    dataAdapterList.Add(key, adapter);
                }
                else
                {
                    dataAdapterList[key] = adapter;
                }
            }
        }

        private IDataAdapter PullAdapterFromCache(Type dataAdapterInterfaceType, DatabaseCategory dbType, DataAccessType daType)
        {
            string key = dataAdapterInterfaceType.FullName + "#" + dbType.ToString() + "#" + daType.ToString();
            IDataAdapter adapter = null;
            dataAdapterList.TryGetValue(key, out adapter);
            return adapter;
        }

        private string GetSuffixByDataBaseType(DatabaseCategory dbType)
        {
            string suffix = string.Empty;
            switch (dbType)
            {
                case DatabaseCategory.SqlServer:
                    suffix = "ForSqlServer";
                    break;
                case DatabaseCategory.MySql:
                    suffix = "ForMySQL";
                    break;
                case DatabaseCategory.Oracle:
                    suffix = "ForOracle";
                    break;
                case DatabaseCategory.Access:
                    suffix = "ForAccess";
                    break;
                case DatabaseCategory.MongoDB:
                    suffix = "ForMongoDB";
                    break;
                default:
                    suffix = dbType.ToString();
                    break;
            }

            return suffix;
        }


        internal DBProvider GetDBProviderByDataBaseType(DatabaseCategory dbType)
        {
            var providerConfig = ConfigFactory.GetConfig<DBProviderConfig>();
            if (providerConfig.DBProviders == null)
            {
                throw new Exception("配置文件关键元素缺失");
            }
            return providerConfig.DBProviders.First(p => p.Category == dbType);
        }

        static void ConfigFactory_OnModified()
        {
            lock (lockObj)
            {
                dataAdapterList.Clear();
            }
        }
    }
}
